#include "textfield.h"

TextField::TextField(std::string id, int x, int y, int size_x, int size_y, std::function<void(std::string)> action,
		ALLEGRO_FONT* text_font, const unsigned char text_color[3], int padding, unsigned maxchars, 
		std::string graphic_active, std::string graphic_inactive)
	: GuiElement(id, x, y, size_x, size_y)
{
	m_padding = padding;
	m_maxchars = maxchars;
	m_graphic_active = graphic_active;
	m_graphic_inactive = graphic_inactive;
	m_action = action;
	m_text_font = text_font;
	m_text_color = al_map_rgb(text_color[0], text_color[1], text_color[2]);

	m_active = false;
}

TextField::~TextField()
{
	al_destroy_font(m_text_font);
}

void TextField::PrimaryMouseButtonDown(int x, int y)
{
	// Did we press the mouse button down on the button?
	bool hit = false;
	if (x >= m_x && x < m_x + m_size_x && y >= m_y && y < m_y + m_size_y)
		hit = true;

	// If yes, it is not pressed. If not, it is not.
	if (hit)
		m_active = true;
	else
		m_active = false;
}

void TextField::CharEntered(int keycode, int unichar)
{
	// Interaction is only possible if the field is active.
	if (m_active)
	{
		// Has enter been pressed?
		if (keycode == ALLEGRO_KEY_ENTER)
		{
			// If so, trigger the action.
			m_action(m_text);
			m_active = false;
		}
		// On backspace, delete one character.
		else if (keycode == ALLEGRO_KEY_BACKSPACE && m_text.length() > 0)
		{
			m_text.resize(m_text.length() - 1);
		}
		// If input is a character, append it to the current text...
		else if (unichar >= 32)
		{
			ALLEGRO_USTR *input = al_ustr_new("");
			al_ustr_append_chr(input, unichar);
		
			char* buffer = new char[m_maxchars];
			al_ustr_to_buffer(input, buffer, m_maxchars);

			std::string append_string(buffer);

			// ...but only if it is not too long to fit in.
			if (append_string.length() + m_text.length() <= m_maxchars)
				m_text.append(buffer);
		}
	}
}

void TextField::DeleteText()
{
	m_text = "";
}

void TextField::Draw()
{
	// Draw the textfield graphic, either active or inactive.
	if (m_active)
	{
		ALLEGRO_BITMAP* graphic = allegro_bitmap_resource_manager.GetResource(m_graphic_active);
		al_draw_scaled_bitmap(graphic, 0, 0, al_get_bitmap_width(graphic), al_get_bitmap_height(graphic),
			m_x, m_y, m_size_x, m_size_y, NULL);
	}
	else
	{
		ALLEGRO_BITMAP* graphic = allegro_bitmap_resource_manager.GetResource(m_graphic_inactive);
		al_draw_scaled_bitmap(graphic, 0, 0, al_get_bitmap_width(graphic), al_get_bitmap_height(graphic),
			m_x, m_y, m_size_x, m_size_y, NULL);
	}

	// If the field is currently selected, we want to show a marker that indicates this and the write position.
	std::string to_draw = m_text;
	if (m_active)
		to_draw.append("|");

	// The bitmap to which the text will be drawn. This is never bigger than the field minus its padding.
	ALLEGRO_BITMAP *text_bitmap = al_create_bitmap(m_size_x - 2 * m_padding, m_size_y - 2 * m_padding);
	al_set_target_bitmap(text_bitmap);

	// If the text would flow out on the right, change the plan: Instead we now write at the right of the field
	// with right alignment so we can always see the head of the text (we want to see what we write). This does not
	// matter if the field is not active (we rather want to see the beginning of the text then).
	if (al_get_text_width(m_text_font, to_draw.c_str()) > m_size_x - 2 * m_padding && m_active)
		al_draw_text(m_text_font, m_text_color, m_size_x - 2 * m_padding, 0,
			ALLEGRO_ALIGN_RIGHT, to_draw.c_str());
	else
		// If the text fits in alright, just draw it.
		al_draw_text(m_text_font, m_text_color, 0, 0, NULL, to_draw.c_str());

	// We are done, set the backbuffer as target again and draw our text to the backbuffer.
	al_set_target_bitmap(al_get_backbuffer(System::GetInstance()->display()));
	al_draw_bitmap(text_bitmap, m_x + m_padding, m_y + m_padding, NULL);

	// The text is not needed anymore so destroy it.
	al_destroy_bitmap(text_bitmap);
}